import { IDBStore } from './store'
import { IDBDataType } from './types'

export class IDBStoreUpdate {
  idbStore: IDBStore
  constructor(idbStore: IDBStore) {
    this.idbStore = idbStore
  }

  /**
   * 覆盖式更新, 如果有主键, 覆盖原来主键那条的数据更新. 否则会新增一条记录.
   * @param record
   * @returns
   */
  update(record: IDBDataType): Promise<IDBDataType> {
    const db = this.idbStore.getDB()
    const storeName = this.idbStore.getStoreName()
    const transaction = db.transaction(storeName, 'readwrite')
    const store = transaction.objectStore(storeName)
    // idb 的 put 已经具备若有则去更新, 若无则插入的类 upsert 功能
    const req = store.put(record)

    return new Promise(function (resolve, reject) {
      req.onsuccess = function (evt) {
        const target = evt.target as IDBRequest
        resolve(target.result)
      }
      req.onerror = function (evt) {
        const target = evt.target as IDBRequest
        reject(target.error)
      }
    })
  }

  // todo bulkUpdate 批量更新

  /**
   * 合并式更新, 如果有主键且找得到记录, 则合并旧对象更新. 否则会新增一条记录.
   * @param record
   * @returns
   */
  merge(record: IDBDataType): Promise<IDBDataType> {
    const db = this.idbStore.getDB()
    const storeName = this.idbStore.getStoreName()
    const transaction = db.transaction(storeName, 'readwrite')
    const store = transaction.objectStore(storeName)
    const schema = this.idbStore.getSchema()
    const keyPath = schema.primaryKey.keyPath
    let result = record
    const readReq = store.get(record[keyPath])
    readReq.onsuccess = function (evt) {
      const target = evt.target as IDBRequest
      result = {
        ...target.result,
        ...record
      }
      store.put(result)
    }
    readReq.onerror = function (_evt) {
      // 忽视错误直接写入
      store.put(result)
    }
    return new Promise(function (resolve, reject) {
      transaction.oncomplete = function () {
        resolve(result)
      }
      // 备注: 如果主动调用 transaction.abort() 取消, 是先触发 onerror 事件后触发 onabort 事件
      // 错误通常是由 request onerror 发起, 冒泡给 transaction onerror 的, 这样的错误通常不需要处理 onabort 事件.
      // 但是有例外, 如果这个错误和特定 request 无关, 如 Quota exceeded 超出配额, 磁盘 I/O 错误, 那么需要主动留意 onabort 的处理.
      transaction.onabort = function (evt) {
        const target = evt.target as IDBTransaction
        if (target.error instanceof Error) {
          reject(target.error)
        } else {
          reject(new Error('TransactionAborted'))
        }
      }
      transaction.onerror = function (evt) {
        const target = evt.target as IDBRequest
        reject(target.error)
      }
    })
  }
}
